package org.codefilarete.stalactite.sql.statement.binder;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.codefilarete.tool.function.SerializableThrowingBiFunction;

/**
 * Wrapper around another {@link ResultSetReader} to handle null value
 * 
 * @author Guillaume Mary
 */
public class NullAwareResultSetReader<T> implements ResultSetReader<T> {
	
	private final ResultSetReader<T> delegate;
	
	public NullAwareResultSetReader(SerializableThrowingBiFunction<ResultSet, String, T, SQLException> resultSetGetter) {
		this(ResultSetReader.ofMethodReference(resultSetGetter));
	}
	
	public NullAwareResultSetReader(ResultSetReader<T> delegate) {
		this.delegate = delegate;
	}
	
	@Override
	public T doGet(ResultSet resultSet, String columnName) throws SQLException {
		if (!isNull(columnName, resultSet)) {
			return getNotNull(columnName, resultSet);
		} else {
			return null;
		}
	}
	
	@Override
	public Class<T> getType() {
		return delegate.getType();
	}
	
	@Override
	public <O> Class<O> getColumnType() {
		return delegate.getColumnType();
	}
	
	/**
	 * Says if a column is null.
	 *
	 * This implementation is done with getObject(columnName) == null. Not sure that all JDBC driver supports it. Official way is to use
	 * {@link ResultSet#wasNull()} but it needs column to be read before. Thought this is more expensive than testing getObject().
	 * To be changed/overridden according to JDBC driver support or performance observation. 
	 *
	 * @throws SQLException the possible error thrown by {@link ResultSet#getObject(String)}
	 */
	protected boolean isNull(String columnName, ResultSet resultSet) throws SQLException {
		return resultSet.getObject(columnName) == null;
	}
	
	public T getNotNull(String columnName, ResultSet resultSet) {
		return delegate.get(resultSet, columnName);
	}
	
}
